package cn.b504.qpcr.controller;

import cn.b504.qpcr.entity.AjaxResult;
import cn.b504.qpcr.entity.AnalysisMethodSetting;
import cn.b504.qpcr.entity.Data;
import cn.b504.qpcr.interfaces.cal.Cal;
import cn.b504.qpcr.interfaces.cal.CalMeanAndSd;
import cn.b504.qpcr.utils.Utils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;

/**
 * @author ssc
 */
@RestController
@RequestMapping("analysis")
public class AnalysisController {
  private final Cal cal;
  private final CalMeanAndSd calMeanAndSd;

  public AnalysisController(Cal cal, CalMeanAndSd calMeanAndSd) {
    this.cal = cal;
    this.calMeanAndSd = calMeanAndSd;
  }

  @RequestMapping("calAll")
  public AjaxResult calAll(@RequestBody Data data) {
    // 计算 Melt Peak(波峰曲线) 和 Melt Temp(波峰最高点温度)
    Future<?> futureMelt = cal.calMeltPeakAndMeltTemperature(data);
    // 校准
    this.calibrate(data);
    //  计算起始和终止循环
    if(data.getAnalysisMethodSetting().getBaseLineAuto()){
      this.calStartEndCycle(data);
    }
    // 计算本底去除、拟合数据
    this.calQuantificationShow(data);
    // 计算 Cq 和 Sq
    this.calCqAndSq(data);
    // 确保异步队列全部完成任务
    Utils.ensureFutureDone(futureMelt);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calCqAndSq")
  public AjaxResult calCqAndSq(@RequestBody Data data) {
    // 计算 Cq
    cal.calCq(data);
    final List<Future<?>> listSq = new ArrayList<>();
    final List<Future<?>> list = new ArrayList<>();
    // [warning] 警告：一定要保证每个孔里的 targets 的 channelIndex 不重复，否则多线程处理会使数据不安全
    data.getTargets().forEach(it -> {
      // 计算 Sq
      listSq.add(cal.calSq(it, data));
      // 计算 Cq Mean(均值) 和 Cq Sd(标准差)
      list.add(calMeanAndSd.cq(it, data));
    });
    // 确保异步队列全部完成任务
    Utils.ensureEmptyFutureList(listSq);
    // 计算 Sq Mean(均值) 和 Sq Sd(标准差)
    data.getTargets().forEach(it -> list.add(calMeanAndSd.sq(it, data)));
    // 确保异步队列全部完成任务
    Utils.ensureEmptyFutureList(list);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calibrate")
  public AjaxResult calibrate(@RequestBody Data data) {
    cal.calibrate(data);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calStartEndCycle")
  public AjaxResult calStartEndCycle(@RequestBody Data data) {
    cal.calStartEndCycle(data);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calCq")
  public AjaxResult calCq(@RequestBody Data data) {
    cal.calCq(data);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calCqMeanAndSd")
  public AjaxResult calCqMeanAndSd(@RequestBody Data data) {
    final List<Future<?>> list = new ArrayList<>();
    // [warning] 警告：一定要保证每个孔里的 targets 的 channelIndex 不重复，否则多线程处理会使数据不安全
    data.getTargets().forEach(it -> list.add(calMeanAndSd.cq(it, data)));
    Utils.ensureEmptyFutureList(list);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calSq")
  public AjaxResult calSq(@RequestBody Data data) {
    final List<Future<?>> list = new ArrayList<>();
    // [warning] 警告：一定要保证每个孔里的 targets 的 channelIndex 不重复，否则多线程处理会使数据不安全
    data.getTargets().forEach(it -> list.add(cal.calSq(it, data)));
    Utils.ensureEmptyFutureList(list);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calSqMeanAndSd")
  public AjaxResult calSqMeanAndSd(@RequestBody Data data) {
    final List<Future<?>> list = new ArrayList<>();
    // [warning] 警告：一定要保证每个孔里的 targets 的 channelIndex 不重复，否则多线程处理会使数据不安全
    data.getTargets().forEach(it -> list.add(calMeanAndSd.sq(it, data)));
    Utils.ensureEmptyFutureList(list);
    return AjaxResult.ok(data);
  }

  @RequestMapping("calMeltPeak")
  public AjaxResult calMeltPeak(@RequestBody Data data) {
    Future<?> future = cal.calMeltPeakAndMeltTemperature(data);
    while (!future.isDone()) {
      Thread.yield();
    }
    return AjaxResult.ok(data);
  }

  @RequestMapping("calQuantificationShow")
  public AjaxResult calQuantificationShow(@RequestBody Data data) {
    final List<Future<?>> list = new ArrayList<>();
    data.getWells().forEach(well -> Arrays.stream(well.getLines()).forEach(it -> list.add(cal.calQuantificationShow(it))));
    Utils.ensureEmptyFutureList(list);
    return AjaxResult.ok(data);
  }
}
